/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package so.n_3.musicbox.model;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.Enumeration;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import javazoom.jl.decoder.JavaLayerException;
import javazoom.jl.player.Player;

/**
 *
 * @author oasynnoum
 */
public class BohemianPlayer extends Thread implements AKindOfPlayer {

    
    protected Player player = null;
    
    protected Vector<Music> queue = new Vector<Music>();
    
    protected int currentQueuePosition = 0;
    
    protected boolean started = false;
    
    protected boolean running = false;
    
    protected boolean stopCurrentMusic = false;
    
    protected long timeForWaiting = 300;
    
    private int queueIndicator = 0;
    
    private Thread queueObserver = null;
    
    private static BohemianPlayer instance = new BohemianPlayer();
    
    public int getPosition() {
        return this.player == null ? 0 : this.player.getPosition();
    }
    
    @Override
    public void start() {
        if (this.started) {
            return;
        }
        this.started = true;
        this.running = false;
        super.start();
        this.queueObserver.start();
    }
    
    @Override
    public void run() {
        while (true) {
            // write code for processing this loop
            if (this.running) {
                if (this.currentQueuePosition < this.queue.size()) {
                    this.play();
                    if (this.queueIndicator == 0) this.nextMusic();
                } else {
                    this.reset();
                }
            }

            try {
                synchronized (this) {
                    wait(this.timeForWaiting);
                }
            } catch (InterruptedException ex) {
                Logger.getLogger(BohemianPlayer.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
    
    @Override
    public boolean isRunning() {
        return this.running;
    }
    
    @Override
    public boolean isStarted() {
        return this.started;
    }
    
    @Override
    public void setRunning(boolean running) {
        this.running = running;
        if (this.player != null)
            this.player.setPause(!this.running);
    }
    
    @Override
    public void add(Playlist playlist) {
        Vector<Music> musics = playlist.getMusics();
        for (int i = 0; i < musics.size(); i++) {
            Music music = musics.get(i);
            this.add(music);
        }
    }
    
    @Override
    public void add(Music music) {
        this.queue.add(music);
    }
    
    @Override
    public int getCurrentQueuePosition() {
        return this.currentQueuePosition;
    }
    
    @Override
    public Music[] getQueue() {
        int queueSize = this.queue.size();
        if (queueSize == 0) {
            return new Music[0];
        }
        
        Music[] musics = new Music[queueSize];
        for (int i = 0; i < queueSize; i++) {
            musics[i] = this.queue.elementAt(i);
        }
        return musics;
    }
    
    @Override
    public boolean clearQueue() {
        stopCurrentMusic();
        this.setCurrentQueuePosition(0);
        this.queue.clear();
        return true;
    }
    
    private synchronized void stopCurrentMusic() {
        if (this.player != null && !this.player.isComplete()) {
            this.player.stop();
            while (!this.player.isComplete()) {
                try {
                    Thread.sleep(this.timeForWaiting);
                } catch (InterruptedException ex) {
                    Logger.getLogger(BohemianPlayer.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
            this.setRunning(false);
        }
        
        if (this.player != null) {
            this.player.close();
        }
    }
    
    @Override
    public void play() {
        if (this.queue.isEmpty()) {
            return;
        }

        this.stopCurrentMusic();

        Music currentMusic = this.queue.elementAt(this.currentQueuePosition);
        try {
            BufferedInputStream currentMusicStream = new BufferedInputStream(new FileInputStream(currentMusic.getUrl()));
            this.player = new Player(currentMusicStream);
            this.setRunning(true);
            this.player.play();
        } catch (JavaLayerException ex) {
            Logger.getLogger(BohemianPlayer.class.getName()).log(Level.SEVERE, null, ex);
        } catch (FileNotFoundException ex) {
            Logger.getLogger(BohemianPlayer.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            if (this.player != null) this.player.close();

        }
    }
    
    @Override
    public synchronized boolean nextMusic() {
        boolean result = this.currentQueuePosition + this.queueIndicator + 1 < this.queue.size();
        this.queueIndicator++;
        return result;
    }
    
    @Override
    public synchronized boolean previousMusic() {
        boolean result = this.currentQueuePosition + this.queueIndicator - 1 >= 0;
        this.queueIndicator--;
        return result;
    }

    public int getQueueIndicator() {
        return this.queueIndicator;
    }
    
    private synchronized void resetQueueIndicator() {
        this.queueIndicator = 0;
    }
    
    public synchronized void setCurrentQueuePosition(int position) {
        if (position < 0 || position >= this.queue.size())
            position = 0;
        this.currentQueuePosition = position;
    }
    
    @Override
    public void reset() {
        this.currentQueuePosition = 0;
    }
    
    private class Observer extends Thread {
        
        private boolean isProcessing = false;
        
        @Override
        public void run() {
            BohemianPlayer player = BohemianPlayer.getInstance();
            while(true) {

                    if (player.getQueueIndicator() != 0 && !this.isProcessing) {
                        this.isProcessing = true;
                        boolean isStopped = player.isRunning();
                        player.stopCurrentMusic();
                        player.setCurrentQueuePosition(
                                player.getCurrentQueuePosition() 
                                + player.getQueueIndicator());
                        player.resetQueueIndicator();
                        if (isStopped)
                            player.setRunning(true);
                        this.isProcessing = false;
                    }

                synchronized (this) {
                    try {
                        wait(player.timeForWaiting);
                    } catch (InterruptedException ex) {
                        Logger.getLogger(BohemianPlayer.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
            }
        }
    }
    
    private BohemianPlayer() {
        this.queueObserver = new BohemianPlayer.Observer();
    }
    
    public static BohemianPlayer getInstance() {
        return BohemianPlayer.instance;
    }
    
}
